package core

import (
	"context"
	"errors"
	"fmt"
	"github.com/spf13/cast"
	"io"
	"server/component"
	"server/im/entity"
	pb "server/im/rpc"
)

func newGateway(gatewayId string, h *Hub, s pb.Hub_ChatServer) *Gateway {
	return &Gateway{
		gatewayId:  gatewayId,
		stream:     s,
		hub:        h,
		outputChan: make(chan entity.CoreOutModel, 1024),
	}
}

type Gateway struct {
	gatewayId  string
	stream     pb.Hub_ChatServer
	hub        *Hub
	outputChan chan entity.CoreOutModel
}

func (o *Gateway) run(ctx context.Context) {
	fmt.Println("core gateway run")
RUN:
	for {
		select {
		case <-ctx.Done(): // 当 context 被取消时执行
			fmt.Println(o.gatewayId+"chat gateway cancelled:", ctx.Err())
		case <-(o.stream).Context().Done(): // 当 context 被取消时执行
			fmt.Println(o.gatewayId+"chat gateway stream cancelled:", ctx.Err())
		default:
			in, err := (o.stream).Recv()
			if err != nil {
				if err != io.EOF {
					// 处理其他错误
					component.Log.Error(fmt.Errorf("read from gateway:%w", err))
				}
				break RUN
			}
			cmd := uint16(in.GatewayInModel.Cmd)
			if in.MsgType == entity.MsgTypeNormal {
				action, ok := RoutesMap[cmd]
				if ok {
					coreIn := entity.CoreInModel{
						GatewayInModel: entity.GatewayInModel{
							Data: in.GatewayInModel.Data,
							Cmd:  uint16(in.GatewayInModel.Cmd),
							Ip:   in.GatewayInModel.Ip,
							User: entity.UserModel{
								Id:    int(in.GatewayInModel.User.Id),
								Name:  in.GatewayInModel.User.Name,
								Token: in.GatewayInModel.User.Token,
								Uuid:  in.GatewayInModel.User.Uuid,
								Ip:    in.GatewayInModel.User.Ip,
							},
							UserUni: in.GatewayInModel.UserUni,
						},
						GatewayId: in.GatewayId,
						MsgType:   int(in.MsgType),
					}
					reqCtx := entity.NewServiceContext(nil, coreIn, o.hub.respondChan)
					go action(reqCtx)
				} else {
					component.Log.Error(errors.New("no action" + cast.ToString(cmd)))
				}
			}
		}
	}
}

func (o *Gateway) write(outModel entity.CoreOutModel) {
	out := &pb.CoreOutModel{
		GatewayOutModel: &pb.GatewayOutModel{
			Data: outModel.GatewayOutModel.Data,
			Cmd:  uint32(outModel.GatewayOutModel.Cmd),
		},
		GatewayId:    outModel.GatewayId,
		MsgType:      uint32(outModel.MsgType),
		ReceiverType: uint32(outModel.ReceiverType),
		Clients:      outModel.Clients,
	}
	if err := o.stream.Send(out); err != nil {
		component.Log.Error(err)
	}
}
